const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { executeClicks, ensureConfigDefaults, loadConfig } = require('./src/click.js');

let isRunning = false;

function sendJson(res, status, data) {
	const body = JSON.stringify(data);
	res.writeHead(status, {
		'Content-Type': 'application/json; charset=utf-8',
		'Content-Length': Buffer.byteLength(body),
	});
	res.end(body);
}

const server = http.createServer(async (req, res) => {
	const parsed = url.parse(req.url, true);
	const pathname = parsed.pathname || '/';

	// Serve static files from ./public
	const publicDir = path.join(__dirname, 'public');
	const safePathname = pathname.replace(/\\/g, '/');
	let staticFilePath = null;
	if (req.method === 'GET') {
		if (safePathname === '/' || safePathname === '/index.html') {
			staticFilePath = path.join(publicDir, 'index.html');
		} else {
			const rel = safePathname.replace(/^\/+/, '');
			const candidate = path.join(publicDir, rel);
			if (candidate.startsWith(publicDir)) {
				staticFilePath = candidate;
			}
		}
		if (staticFilePath && fs.existsSync(staticFilePath) && fs.statSync(staticFilePath).isFile()) {
			const ext = path.extname(staticFilePath).toLowerCase();
			const mime = {
				'.html': 'text/html; charset=utf-8',
				'.css': 'text/css; charset=utf-8',
				'.js': 'application/javascript; charset=utf-8',
				'.json': 'application/json; charset=utf-8',
				'.png': 'image/png',
				'.jpg': 'image/jpeg',
				'.jpeg': 'image/jpeg',
				'.svg': 'image/svg+xml',
			}[(ext || '')] || 'application/octet-stream';
			try {
				const content = fs.readFileSync(staticFilePath);
				res.writeHead(200, { 'Content-Type': mime, 'Content-Length': content.length });
				res.end(content);
				return;
			} catch (e) {
				res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
				res.end('Static file error');
				return;
			}
		}
	}

	if (req.method === 'GET' && pathname === '/click') {
		if (isRunning) {
			return sendJson(res, 409, { ok: false, message: 'Click task is already running' });
		}

		const cfg = ensureConfigDefaults(loadConfig());
		const points = cfg.clickPoints || [];
		if (!Array.isArray(points) || points.length === 0) {
			return sendJson(res, 400, { ok: false, message: 'No saved click points. Please record with c2.js first.' });
		}

		const options = {
			points,
			cycles: 1,
			perPointClicks: 1,
			delayBetweenClicksMs: 50,
			delayBetweenPointsMs: 200,
			delayBetweenCyclesMs: 500,
			applyOffset: false,
			enableCalibration: false,
			coordinateOffset: cfg.coordinateOffset,
			fineTune: cfg.fineTune,
			logger: (line) => { console.log(line); },
		};

		// Fire-and-forget execution, prevent concurrent runs
		isRunning = true;
		executeClicks(options)
			.catch((e) => { console.error('Click task error:', e); })
			.finally(() => { isRunning = false; });

		return sendJson(res, 200, { ok: true, message: 'Click task started' });
	}

	// 获取点击点列表（只读，含单/双击状态）
	if (req.method === 'GET' && pathname === '/click-points') {
		const cfg = ensureConfigDefaults(loadConfig());
		const points = Array.isArray(cfg.clickPoints) ? cfg.clickPoints : [];
		const items = points.map((p, index) => ({ index, x: p.x, y: p.y, mode: p && p.mode === 2 ? 2 : 1 }));
		return sendJson(res, 200, { ok: true, items });
	}

	// 更新点击点的单/双击模式（仅修改 mode 字段）
	if (req.method === 'POST' && pathname === '/click-points') {
		let raw = '';
		req.on('data', (chunk) => {
			raw += chunk;
			if (raw.length > 1_000_000) {
				res.writeHead(413, { 'Content-Type': 'application/json; charset=utf-8' });
				res.end(JSON.stringify({ ok: false, message: 'Payload too large' }));
				try { req.destroy(); } catch (_) {}
			}
		});
		req.on('end', () => {
			try {
				const body = raw ? JSON.parse(raw) : {};
				const updates = Array.isArray(body && body.items) ? body.items : [];
				const cfg = ensureConfigDefaults(loadConfig());
				if (!Array.isArray(cfg.clickPoints)) cfg.clickPoints = [];
				for (const u of updates) {
					if (!u || typeof u.index !== 'number') continue;
					const i = u.index | 0;
					if (i < 0 || i >= cfg.clickPoints.length) continue;
					const mode = u.mode === 2 ? 2 : 1;
					cfg.clickPoints[i] = { ...cfg.clickPoints[i], mode };
				}
				fs.writeFileSync(path.join(__dirname, 'src', 'config.json'), JSON.stringify(cfg, null, 2), 'utf8');
				const items = cfg.clickPoints.map((p, index) => ({ index, x: p.x, y: p.y, mode: p && p.mode === 2 ? 2 : 1 }));
				return sendJson(res, 200, { ok: true, items });
			} catch (e) {
				return sendJson(res, 400, { ok: false, message: 'Invalid payload', error: String(e && e.message ? e.message : e) });
			}
		});
		return;
	}

	// PM2重启接口
	if (req.method === 'POST' && pathname === '/pm2-restart') {
		try {
			const { exec } = require('child_process');
			exec('pm2 restart index', (error, stdout, stderr) => {
				if (error) {
					return sendJson(res, 500, { ok: false, message: '执行PM2重启命令失败', error: error.message });
				}
				// 重启成功后，立即执行日志命令
				exec('pm2 logs --lines 20 --nostream', (logError, logStdout, logStderr) => {
					if (logError) {
						return sendJson(res, 200, { 
							ok: true, 
							message: '重启成功，但获取日志失败', 
							restartOutput: stdout, 
							restartError: stderr,
							logError: logError.message 
						});
					}
					return sendJson(res, 200, { 
						ok: true, 
						message: '重启成功', 
						restartOutput: stdout, 
						restartError: stderr,
						logOutput: logStdout,
						logError: logStderr
					});
				});
			});
		} catch (e) {
			return sendJson(res, 500, { ok: false, message: 'PM2重启接口错误', error: String(e) });
		}
		return;
	}

	sendJson(res, 404, { ok: false, message: 'Not Found' });
});

const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 7777;
server.listen(PORT, '0.0.0.0', () => {
	console.log(`HTTP server listening on:`);
	console.log(`  Local:   http://localhost:${PORT}`);
	try {
		const ifaces = os.networkInterfaces();
		const addrs = [];
		for (const name of Object.keys(ifaces)) {
			for (const info of ifaces[name] || []) {
				if (info && info.family === 'IPv4' && !info.internal && info.address) {
					addrs.push(info.address);
				}
			}
		}
		if (addrs.length) {
			for (const a of addrs) {
				console.log(`  LAN:     http://${a}:${PORT}`);
			}
		} else {
			console.log(`  LAN:     (no active IPv4 found)`);
		}
	} catch (e) {
		console.log('  LAN:     (error listing interfaces)');
	}
});


